﻿Function Global:Add-WSDLReference {
	param( 
		[string] $wsdlLocation, 
		[string] $namespace)
	
	## Create the web service cache, if it doesn't already exist
	Invoke-Expression (
	'if(-not (Test-Path Variable:\'+$namespace+'.WebServiceCache))	
	{
		${GLOBAL:'+$namespace+'.WebServiceCache} = @{}
	}')
	
	## Check if there was an instance from a previous connection to
	## this web service. If so, return that instead.
	Invoke-Expression (
	'$oldInstance = ${GLOBAL:'+$namespace+'.WebServiceCache}[$wsdlLocation]'
	)
	if($oldInstance)
	{
		$oldInstance
		return
	}
	
	## Load the required Web Services DLL
	[void] [Reflection.Assembly]::LoadWithPartialName("System.Web.Services")
	
	## Download the WSDL for the service, and create a service description from it.
	$wc = new-object System.Net.WebClient
	$wc.Proxy = [System.Net.WebRequest]::DefaultWebProxy;
	$wc.Credentials = [System.Net.CredentialCache]::DefaultCredentials;
	$wsdlStream = $wc.OpenRead($wsdlLocation);
	
	# Ensure that we were able to fetch the WSDL
	if(-not (Test-Path Variable:\wsdlStream))
	{
		return
	}
	$serviceDescription = [Web.Services.Description.ServiceDescription]::Read($wsdlStream)
	$wsdlStream.Close()
	
	## Ensure that we were able to read the WSDL into a service description
	if(-not (Test-Path Variable:\serviceDescription))
	{
		return
	}
	
	## Import the web service into a CodeDom
	$serviceNamespace = New-Object System.CodeDom.CodeNamespace
	if($namespace)
	{
		$serviceNamespace.Name = $namespace
	}
	
	$codeCompileUnit = New-Object System.CodeDom.CodeCompileUnit

	$serviceDescriptionImporter = New-Object Web.Services.Description.ServiceDescriptionImporter	
	$serviceDescriptionImporter.AddServiceDescription($serviceDescription, $null, $null)
	#$serviceDescriptionImporter.AddServiceDescription($oldWsdl, $null, $null)
	
	[void] $codeCompileUnit.Namespaces.Add($serviceNamespace)
	[void] $serviceDescriptionImporter.Import($serviceNamespace, $codeCompileUnit)
	
	## Generate the code from that CodeDom into a string
	$generatedCode = New-Object Text.StringBuilder
	$stringWriter = New-Object IO.StringWriter $generatedCode
	$provider = New-Object Microsoft.CSharp.CSharpCodeProvider
	$provider.GenerateCodeFromCompileUnit($codeCompileUnit, $stringWriter, $null)
	
	## Compile the source code.
	
	$references = @("System.dll", "System.Web.Services.dll", "System.Xml.dll")
	$compilerParameters = New-Object System.CodeDom.Compiler.CompilerParameters
	$compilerParameters.ReferencedAssemblies.AddRange($references)
	$compilerParameters.GenerateInMemory = $true
	
	$compilerResults = $provider.CompileAssemblyFromSource($compilerParameters, $generatedCode)
	
	## Write any errors if generated.        
	if($compilerResults.Errors.Count -gt 0)
	{
		$errorLines = ""
		foreach($error in $compilerResults.Errors)
		{
			$errorLines += "`n`t" + $error.Line + ":`t" + $error.ErrorText
		}
		Write-Error $errorLines
		return
	}
	## There were no errors.  Create the webservice object and return it.
	else
	{
		## Get the assembly that we just compiled
		$assembly = $compilerResults.CompiledAssembly
	
		## Find the type that had the WebServiceBindingAttribute.
		## There may be other "helper types" in this file, but they will
		## not have this attribute
		$type = $assembly.GetTypes() |
			Where-Object { $_.GetCustomAttributes(
				[System.Web.Services.WebServiceBindingAttribute], $false) }
	
		if(-not $type)
		{
			Write-Error "Could not generate web service proxy."
			return
		}	
		## Create an instance of the type, store it in the cache,
		## and return it to the user.
		$instance = $assembly.CreateInstance($type)
		Invoke-Expression ('${GLOBAL:'+$namespace+'.WebServiceCache}[$wsdlLocation] = $instance')
		
	}
	
}